{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# 题目1\n",
    "form.jpg是正常的模板，scanned-form.jpg是采集时拍摄的图片，需要对它进行校正处理。"
   ],
   "id": "cffba85d035c387d"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-11-03T03:51:21.018007Z",
     "start_time": "2025-11-03T03:51:18.274870Z"
    }
   },
   "cell_type": "code",
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "def order_points(pts):\n",
    "    \"\"\"对轮廓点进行排序\"\"\"\n",
    "    rect = np.zeros((4, 2), dtype=\"float32\")\n",
    "\n",
    "    s = pts.sum(axis=1)\n",
    "    rect[0] = pts[np.argmin(s)]  # 左上\n",
    "    rect[2] = pts[np.argmax(s)]  # 右下\n",
    "\n",
    "    diff = np.diff(pts, axis=1)\n",
    "    rect[1] = pts[np.argmin(diff)]  # 右上\n",
    "    rect[3] = pts[np.argmax(diff)]  # 左下\n",
    "\n",
    "    return rect\n",
    "\n",
    "def four_point_transform(image, pts):\n",
    "    \"\"\"应用透视变换校正图像\"\"\"\n",
    "    rect = order_points(pts)\n",
    "    (tl, tr, br, bl) = rect\n",
    "\n",
    "    # 计算目标宽度和高度\n",
    "    widthA = np.linalg.norm(br - bl)\n",
    "    widthB = np.linalg.norm(tr - tl)\n",
    "    maxWidth = max(int(widthA), int(widthB))\n",
    "\n",
    "    heightA = np.linalg.norm(tr - br)\n",
    "    heightB = np.linalg.norm(tl - bl)\n",
    "    maxHeight = max(int(heightA), int(heightB))\n",
    "\n",
    "    # 定义目标点\n",
    "    dst = np.array([\n",
    "        [0, 0],\n",
    "        [maxWidth - 1, 0],\n",
    "        [maxWidth - 1, maxHeight - 1],\n",
    "        [0, maxHeight - 1]], dtype=\"float32\")\n",
    "\n",
    "    # 透视变换\n",
    "    M = cv2.getPerspectiveTransform(rect, dst)\n",
    "    return cv2.warpPerspective(image, M, (maxWidth, maxHeight))\n",
    "\n",
    "def correct_form(input_path, output_path):\n",
    "    \"\"\"校正表单图像主函数\"\"\"\n",
    "    # 读取图像\n",
    "    image = cv2.imread(input_path)\n",
    "    if image is None:\n",
    "        print(f\"无法读取图像: {input_path}\")\n",
    "        return\n",
    "\n",
    "    orig = image.copy()\n",
    "    h, w = image.shape[:2]\n",
    "\n",
    "    # 调整大小（保持比例）\n",
    "    scale = 500 / h\n",
    "    resized = cv2.resize(image, (int(w * scale), 500))\n",
    "\n",
    "    # 预处理\n",
    "    gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)\n",
    "    gray = cv2.GaussianBlur(gray, (5, 5), 0)\n",
    "    edged = cv2.Canny(gray, 75, 200)\n",
    "\n",
    "    # 寻找轮廓\n",
    "    contours, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    # 按面积排序取前5个\n",
    "    contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]\n",
    "\n",
    "    # 寻找四边形轮廓\n",
    "    screen_cnt = None\n",
    "    for c in contours:\n",
    "        perimeter = cv2.arcLength(c, True)\n",
    "        approx = cv2.approxPolyDP(c, 0.02 * perimeter, True)\n",
    "        if len(approx) == 4:\n",
    "            screen_cnt = approx\n",
    "            break\n",
    "\n",
    "    if screen_cnt is None:\n",
    "        print(\"无法找到文档轮廓\")\n",
    "        return\n",
    "\n",
    "    # 还原坐标比例并校正\n",
    "    pts = screen_cnt.reshape(4, 2) / scale\n",
    "    warped = four_point_transform(orig, pts)\n",
    "\n",
    "    # 后处理并保存\n",
    "    warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)\n",
    "    warped = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]\n",
    "    cv2.imwrite(output_path, warped)\n",
    "    print(f\"校正完成，已保存至: {output_path}\")\n",
    "\n",
    "    # 显示结果\n",
    "    cv2.imshow(\"原图\", cv2.resize(orig, (600, 800)))\n",
    "    cv2.imshow(\"校正后\", cv2.resize(warped, (600, 800)))\n",
    "    cv2.waitKey(0)\n",
    "    cv2.destroyAllWindows()\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    correct_form(\"scanned-form.jpg\", \"corrected-form.jpg\")\n"
   ],
   "id": "dd758e24c9c056c1",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "校正完成，已保存至: corrected-form.jpg\n"
     ]
    }
   ],
   "execution_count": 1
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
